#ifndef _COOMM_HPP_
#define _COOMM_HPP_

#include <iostream>

#include <cerrno>
#include <cstring>
#include <cstdlib>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

#define PATHNAME "."
#define PROJ_ID 314159
#define MAXSZIE 4096

// 获取共享内存的key值
key_t getKey()
{
    key_t k = ftok(PATHNAME, PROJ_ID); // 通过name和id让两个进程获取相同的key
    if (k < 0)
    {
        // cin, cout, cerr -> stdin stdout stderr -> 0, 1, 2
        // errno:错误码
        // strerror：获取错误码对应的错误信息
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        exit(1);
    }
}

// 创建并获取共享内存的id
int getShmHelper(key_t k, int flags)
{
    // key是要通过shmget, 设置进入共享内存的属性中！用来表示该共享内存在内核中的唯一性
    // shmid:让用户表示共享内存
    //  shmid vs key
    //  fd    vs inode
    // 为什么要这么做？ 就像我们为什么不直接用身份证标识而用名字所有人一样
    // shmid 和 key 不会相互干扰，有利于解耦
    int shmid = shmget(k, MAXSZIE, flags);
    if (shmid < 0)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        exit(2);
    }
    return shmid;
}

int getShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT);
}

int creatShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT | IPC_EXCL | 0666);
}

// 关联共享内存和地址空间
void *attachShm(int shmid)
{
    //纯数字是没有任何意义的！必须有类型才有意义！
    //我有100，这没有意义，我有100$才有意义！
    100;    //字面值
    10u;
    3.14f;

    void *mem = shmat(shmid, nullptr, 0); //64位系统，指针占用8位字节
    if((long long)mem == -1L)
    {
        std::cerr << "atshm:" << errno << ":" << strerror(errno) << std::endl;
        exit(3);
    }
    return mem;
}

//去关联进程虚拟地址空间和共享内存的映射
void detachShm(void* start)
{
    if(shmdt(start) == -1)
    {
        std::cerr << "dtshm:" << errno << ":" << strerror(errno) << std::endl;
        exit(4);
    }
}

// 删除共享内存
void delShm(int shmid)
{
    if (shmctl(shmid, IPC_RMID, nullptr) == -1)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
    }
}

#endif